home *** CD-ROM | disk | FTP | other *** search
/ Symantec Visual Cafe for Java 2.5 / symantec-visual-cafe-2.5-database-dev-edition.iso / Visual Cafe Pro v1.0 / SOURCE.BIN / BasicCell.java < prev    next >
Encoding:
Java Source  |  1997-06-19  |  21.8 KB  |  685 lines

  1. /*
  2.  * BasicCell.java   1.0   12 Jan 1997
  3.  *
  4.  * Copyright (c) 1996 Krumel & Associates, Inc.  All Rights Reserved.
  5.  *
  6.  * This software is provided as is.  Krumel & Associates shall not be liable
  7.  * for any damages suffered by licensee as a result of using, modifying or
  8.  * distributing this software or its derivatives.
  9.  */
  10. package symantec.itools.db.awt;
  11.  
  12. import java.awt.*;
  13.  
  14. public class BasicCell implements TableCell, java.awt.image.ImageObserver {
  15.     Grid                view;
  16.     DataSource          dataSource;
  17.     Coordinate          coords;
  18.     boolean             selected;
  19.     boolean             keyPressedYet;
  20.     boolean             loseFocusOnArrow;
  21.     int                 cursorPos;
  22.     int                 hlFirst, hlLast;     //left & right for highlighting
  23.     boolean             selectionMade;
  24.     int                 offset;   //will be used for keeping cursor in cell
  25.     int                 chopIndex;
  26.     int                 toLeftOfCell;
  27.     int                 startX;
  28.     boolean             defaultCell;
  29.     int                 type;
  30.  
  31.     static final int    PADSIDES = 5;
  32.  
  33.     public BasicCell(Grid tv, DataSource ds) {
  34.         view = tv;
  35.         dataSource = ds;
  36.     }
  37.  
  38.     public TableCell cloneCell() {
  39.         BasicCell bs = new BasicCell(view, dataSource);
  40.  
  41.         if (coords != null) {
  42.             bs.coords = new Coordinate(coords.row, coords.col);
  43.         }
  44.  
  45.         bs.selected = selected;
  46.         bs.keyPressedYet = keyPressedYet;
  47.         bs.loseFocusOnArrow = loseFocusOnArrow;
  48.         bs.cursorPos = cursorPos;
  49.         bs.selectionMade = selectionMade;
  50.         bs.offset = offset;
  51.         bs.type = type;
  52.  
  53.         return bs;
  54.     }
  55.  
  56.     public int type() {
  57.         return type;
  58.     }
  59.  
  60.     public int type(int t) {
  61.         if (t > CORNER_CELL || t < 0) {
  62.             throw new IllegalArgumentException("Invalid cell type");
  63.         }
  64.  
  65.         return type = t;
  66.     }
  67.  
  68.  
  69.     public void setGrid(Grid v, DataSource ds) {
  70.         view = v;
  71.         dataSource = ds;
  72.     }
  73.  
  74.     public void setDefaultFlag() {
  75.         defaultCell = true;
  76.     }
  77.  
  78.     public void reset() {
  79.         selected = false;
  80.         keyPressedYet = false;
  81.         loseFocusOnArrow = false;
  82.         cursorPos = 0;
  83.         selectionMade = false;
  84.         offset = 0;
  85.     }
  86.  
  87.     public boolean isCellTypeEditable() { return true; }
  88.  
  89.     public void setCoordinates(Coordinate c) {
  90.         coords = c;
  91.     }
  92.  
  93.     public Coordinate getCoordinates() {
  94.         return coords;
  95.     }
  96.  
  97.     public int row() { return coords.row; }
  98.  
  99.     public void setRow(int r) { coords.row = r; }
  100.  
  101.     public int col() { return coords.col; }
  102.  
  103.     public void setCol(int c) { coords.col = c; }
  104.  
  105.     public boolean mouseEvent(Event e) {
  106.         try {
  107.             switch(e.id) {
  108.                 case Event.MOUSE_DOWN:
  109.                     if (!e.shiftDown()) {
  110.                         //if still highlighted position cursor right place and unhighlight
  111.                         keyPressedYet= true;
  112.                         view.setCapture();
  113.                         cursorPos = findCursorPos(e.x);
  114.                         selectionMade = false;
  115.                     } else {
  116.                         //shift pressed so we make a hightlighted region
  117.                         hlFirst = cursorPos;
  118.                         hlLast = findCursorPos(e.x);
  119.                         selectionMade = true;
  120.                     }
  121.  
  122.                     view.redrawCell(this);
  123.                     view.generateEvent(e, view.CELL_MOUSE_DOWN, this);
  124.                     break;
  125.  
  126.                  case view.CELL_MOUSE_DOUBLE:
  127.                     keyPressedYet = false;
  128.                     view.redrawCell(this);
  129.                     offset = 0;
  130.                     view.generateEvent(e, view.CELL_MOUSE_DOUBLE, this);
  131.                     break;
  132.  
  133.                 case Event.MOUSE_DRAG:
  134.                     if (!selectionMade && findCursorPos(e.x) != cursorPos) {
  135.                         //select a region
  136.                         hlFirst = cursorPos;
  137.                         selectionMade = true;
  138.                     }
  139.  
  140.                     hlLast = cursorPos = findCursorPos(e.x);
  141.                     view.redrawCell(this);
  142.                     view.generateEvent(e, view.CELL_MOUSE_DRAG, this);
  143.                     break;
  144.  
  145.                 case Event.MOUSE_UP:
  146.                     view.generateEvent(e, view.CELL_MOUSE_UP, this);
  147.                     break;
  148.             }
  149.         } catch(DataNotAvailable ex) {
  150.             view.handleException(row(), col(), ex);
  151.         }
  152.  
  153.         return true;
  154.     }
  155.  
  156.     int findCursorPos(int x) throws DataNotAvailable {
  157.         if (x < -offset) {
  158.             return 0;
  159.         }
  160.  
  161.         int total = offset + x;
  162.         FontMetrics fm = view.getCellFontMetrics(this);
  163.         Data data = readData();
  164.         String text = data.toString();
  165.         int len = text.length();
  166.  
  167.         //loop through each letter and determine where cursor goes
  168.         Image im = data.toImage();
  169.         int align = view.getCellAlignment(this);
  170.         if (align == Grid.LEFT) {
  171.             if (offset == 0) {
  172.                 total -= PADSIDES + 2;
  173.  
  174.                 if (im != null) {
  175.                     int imageOffset = im.getWidth(this) + 2;
  176.                     int w = view.getColumnWidth(getCoordinates().col);
  177.                     int sw = fm.stringWidth(text);
  178.  
  179.                     if (imageOffset+sw+2+PADSIDES <= w) {
  180.                         total -= imageOffset;
  181.                     }
  182.                 }
  183.             }
  184.         } else
  185.         if (align == Grid.RIGHT) {
  186.             int sw = fm.stringWidth(text);
  187.             int w = view.getColumnWidth(getCoordinates().col);
  188.             if (sw <= w - PADSIDES*2) {
  189.                 total = total - w + sw + PADSIDES;
  190.             }
  191.         } else if (align == Grid.CENTER) {
  192.             int sw = fm.stringWidth(text);
  193.             int w = view.getColumnWidth(getCoordinates().col);
  194.             if (sw<=w) {
  195.                 total = total - (w-sw)/2;
  196.             }
  197.         }
  198.  
  199.         //check if all the way to left
  200.         if (total <= 0) { return 0; }
  201.         for(int i=1; i<=len ; i++) {
  202.             if (fm.stringWidth(text.substring(0, i-1) + fm.charWidth(text.charAt(i-1))/2) > total)
  203.                 return i;
  204.         }
  205.  
  206.         return len;
  207.     }
  208.  
  209.     public Data getData() throws DataNotAvailable {
  210.         return dataSource.getData(coords);
  211.     }
  212.  
  213.     public Data readData() throws DataNotAvailable {
  214.         return dataSource.readData(coords.row, coords.col);
  215.     }
  216.  
  217.     void deleteChar(Data data) {
  218.         if (cursorPos == 0)
  219.             return;
  220.  
  221.         data.deleteChar(cursorPos);
  222.         cursorPos--;
  223.     }
  224.  
  225.     void deleteSelection(Data data) {
  226.         int hlLeft = Math.min(hlFirst, hlLast);
  227.         int hlRight = Math.max(hlFirst, hlLast);
  228.         for (int i=hlLeft; i<hlRight; i++) {
  229.             data.deleteChar(hlLeft+1);
  230.         }
  231.  
  232.         cursorPos = hlLeft;
  233.     }
  234.  
  235.     public boolean keyEvent(Event e) {
  236.         Data        data;
  237.  
  238.         try {
  239.             data = getData();
  240.         } catch (DataNotAvailable ex) {
  241.             view.handleException(row(), col(), ex);
  242.             return true;
  243.         }
  244.  
  245.         char c = (char)e.key;
  246.         boolean changed = false;
  247.         boolean handled = false;
  248.         boolean editable = data.isEditable(coords.row, coords.col)
  249.                            && view.getCellEditable(this);
  250.  
  251.         if (!editable && (e.id == e.KEY_ACTION || e.id == e.KEY_PRESS)) {
  252.             return view.generateEvent(e, view.CELL_KEY_DOWN, this);
  253.         } else if (!editable && e.id == e.KEY_RELEASE) {
  254.             return view.generateEvent(e, view.CELL_KEY_UP, this);
  255.         }
  256.  
  257.         if (e.id == e.KEY_ACTION) {
  258.             if (e.shiftDown() && !selectionMade) {
  259.                 selectionMade = true;
  260.                 hlFirst = hlLast = cursorPos;
  261.             } else if (!e.shiftDown()) {
  262.                 selectionMade = false;
  263.             }
  264.  
  265.             switch(c) {
  266.                 case e.LEFT:
  267.                     if (cursorPos > 0) {
  268.                        cursorPos--;
  269.                        changed = true;
  270.                     }
  271.                     break;
  272.                 case e.RIGHT:
  273.                     if (cursorPos < data.toString().length()) {
  274.                        cursorPos++;
  275.                        changed = true;
  276.                     }
  277.                     break;
  278.                 case e.UP:
  279.                 case e.HOME:
  280.                     if (cursorPos != 0) {
  281.                         cursorPos = 0;
  282.                         changed = true;
  283.                     }
  284.                     break;
  285.                 case e.DOWN:
  286.                 case e.END:
  287.                     int len = data.toString().length();
  288.                     if (cursorPos != len) {
  289.                         cursorPos = len;
  290.                         changed = true;
  291.                     }
  292.                     break;
  293.             }
  294.  
  295.             if (selectionMade) hlLast = cursorPos;
  296.             if (changed) {
  297.                 view.redrawCell(this);
  298.             }
  299.  
  300.             return view.generateEvent(e, view.CELL_KEY_DOWN, this);
  301.         }
  302.         if (e.id == Event.KEY_PRESS) {
  303.             if (!keyPressedYet) {
  304.                 //NEED TO STRIP OUT ANY NON PRINTABLE CHARACTERS!!!
  305.                 if (e.key<33  || e.key>122) {
  306.                     //not printable so ignore
  307.                     return view.generateEvent(e, view.CELL_KEY_DOWN, this);
  308.                 }
  309.  
  310.                 data.clearText();
  311.                 data.appendChar(c);
  312.                 keyPressedYet = true;
  313.                 view.setCapture();
  314.                 cursorPos = 1;      //start at beginning
  315.             } else {
  316.                 if (selectionMade) {
  317.                     deleteSelection(data);
  318.                     selectionMade = false;
  319.                     //if b.s. or del then delete highlight and return
  320.                     if (c == 8 || c == 127) {
  321.                         return view.generateEvent(e, view.CELL_KEY_DOWN, this);
  322.                     }
  323.                 }
  324.  
  325.                 if (e.key == 27 /*esc*/) {
  326.                     cursorPos = 0;
  327.                     keyPressedYet = false;
  328.                     handled = view.generateEvent(e, view.UNDO_CELL_EVENT, this);
  329.                 } else if (c == 8 /*backspace*/) {
  330.                     deleteChar(data);
  331.                     handled = view.generateEvent(e, view.CELL_CONTENT_CHANGE, this);
  332.                 } else if (c == 10 /*enter*/) {
  333.                     try {
  334.                         dataSource.commitData();
  335.                     } catch(Exception ex) {
  336.                         view.handleException(row(), col(), ex);
  337.                     }
  338.                 } else if (c == 127/*delete*/) {
  339.                     if (cursorPos != data.toString().length()) {
  340.                         cursorPos++;
  341.                         deleteChar(data);
  342.                         handled = view.generateEvent(e, view.CELL_CONTENT_CHANGE, this);
  343.                     }
  344.                 } else if (cursorPos == data.toString().length()) {
  345.                     data.appendChar(c);
  346.                     cursorPos++;
  347.                     handled = view.generateEvent(e, view.CELL_CONTENT_CHANGE, this);
  348.                 } else {
  349.                     data.insertChar(cursorPos, c);
  350.                     cursorPos++;
  351.                 }
  352.             }
  353.  
  354.             view.redrawCell(this);
  355.             handled |= view.generateEvent(e, view.CELL_KEY_DOWN, this);
  356.         } else if (e.id == Event.KEY_RELEASE) {
  357.             view.generateEvent(e, view.CELL_KEY_UP, this);
  358.         }
  359.  
  360.         return handled;
  361.     }
  362.  
  363.     public boolean loseFocusOnArrow() {
  364.         return selected & !keyPressedYet;
  365.     }
  366.  
  367.     public void activateCursor() {
  368.         selected = true;
  369.         view.redrawCell(this);
  370.     }
  371.  
  372.     public void deactivateCursor() {
  373.         selected = false;
  374.         view.redrawCell(this);
  375.     }
  376.  
  377.     public boolean canLoseFocus() {
  378.         //try to commit data - if successful then return true
  379.         try {
  380.             //commit dataSource's current data and tell the data to commit
  381.             //also
  382.             dataSource.commitData();
  383.         } catch(Exception ex) {
  384.             //Can throw RuntimeExceptions so catch everything
  385.             view.handleException(row(), col(), ex);
  386.             return false;
  387.         }
  388.  
  389.         return true;
  390.     }
  391.  
  392.     public boolean focusEvent(Event e) {
  393.         if (e.id == Event.GOT_FOCUS) {
  394.             selected = true;
  395.             view.generateEvent(e, view.GOT_CELL_FOCUS, this);
  396.             view.redrawCell(this);
  397.         } else {
  398.             selected = false;
  399.             keyPressedYet = false;
  400.             selectionMade = false;
  401.             offset = 0;
  402.             cursorPos = 0;
  403.             view.generateEvent(e, view.LOST_CELL_FOCUS, this);
  404.             try {
  405.                 dataSource.commitData();
  406.             } catch(Exception ex) {
  407.                 view.handleException(row(), col(), ex);
  408.             }
  409.             view.redrawAroundCell(this);
  410.         }
  411.  
  412.         return true;
  413.     }
  414.  
  415.     public boolean actionEvent(Event e) {
  416.         keyPressedYet = false;
  417.         view.redrawCell(this);
  418.         offset = 0;
  419.         return true;
  420.     }
  421.  
  422.     public void drawCell(Graphics g, CellHints hints) {
  423.         Data        data;
  424.  
  425.         try {
  426.             data = readData();
  427.         } catch (DataNotAvailable ex) {
  428.             view.handleException(row(), col(), ex);
  429.             data = new ImageStringData(dataSource, "");
  430.         }
  431.  
  432.         Rectangle   r = hints.bounds();
  433.         FontMetrics fm = view.getCellFontMetrics(this);
  434.         int         asc = fm.getAscent() + 1;
  435.         int         sw = fm.stringWidth(data.toString());
  436.         int         imageOffset = 0;
  437.         Color       oldfg = hints.fg;
  438.         int         origX = r.x,
  439.                     origWidth = r.width;
  440.  
  441.         hints.setBackground(g);
  442.         g.fillRect(r.x, r.y, r.width-1, r.height-1);
  443.         Image im = data.toImage();
  444.         switch(hints.alignment()) {
  445.             case Grid.LEFT:
  446.                 if (im != null) {
  447.                     imageOffset = im.getWidth(this) + 2;
  448.                 }
  449.  
  450.                 //check to see if it will all fit within cell
  451.                 if (imageOffset+sw+2+PADSIDES <= r.width && im != null) {
  452.                     r.x += PADSIDES;
  453.                     r.y += 1;
  454.                     g.drawImage(im, r.x, r.y, this);
  455.                     r.y -= 1;
  456.                 } else {
  457.                     imageOffset = 0;
  458.                 }
  459.  
  460.                 r.x = origX + imageOffset + PADSIDES;
  461.                 break;
  462.             case Grid.CENTER:
  463.                 if (sw>(r.width - PADSIDES*2)) {
  464.                     r.x += PADSIDES;
  465.                 } else {
  466.                     r.x = r.x + (r.width-sw)/2;
  467.                 }
  468.                 break;
  469.             case Grid.RIGHT:
  470.                 if (im != null) {
  471.                     imageOffset = im.getWidth(this);
  472.                 }
  473.                 //the 3 is just a fudge factor - there must be a better way!!!!
  474.                 //check to see if it will all fit within cell
  475.                 if ((imageOffset+2+sw+PADSIDES*2+3) <= r.width && im != null) {
  476.                     r.x = r.x + r.width - sw - imageOffset - 2 - PADSIDES - 3;
  477.                     r.y += 1;
  478.                     g.drawImage(im, r.x, r.y, this);
  479.                     r.y -= 1;
  480.                     r.x = origX + r.width - sw - PADSIDES - 3;
  481.                 } else {
  482.                     imageOffset = 0;
  483.                     if (sw>(r.width - PADSIDES*2)) {
  484.                         r.x += PADSIDES;
  485.                     } else {
  486.                         r.x = origX + r.width - sw - PADSIDES - 3;
  487.                     }
  488.                 }
  489.                 break;
  490.         }
  491.  
  492.         hints.setForeground(g);
  493.         r.width = r.width + origX - r.x - PADSIDES;
  494.         startX = r.x;
  495.         drawText(data, g, r, fm, hints);
  496.         r.width = origWidth;
  497.  
  498.         //draw cursor
  499.         if (selected && keyPressedYet) {
  500.             int cpos = 0;
  501.             if (data.toString().length() > 0) {
  502.                 cpos = fm.stringWidth(data.subString(toLeftOfCell, cursorPos));
  503.             }
  504.             g.drawLine(r.x + cpos,
  505.                        r.y + 2,
  506.                        r.x + cpos,
  507.                        r.y + fm.getHeight());
  508.         }
  509.  
  510.  
  511.         r.x = origX;
  512.  
  513.         hints.drawBoundary(g);
  514. //The new way is slower but more flexible - some future enhancements to the
  515. //drawing algorithms in the Grid class should make the slowdown
  516. //insignificant soon enough.
  517. //           g.setColor(Color.gray);
  518. //        g.drawRect(r.x, r.y, r.width-1, r.height-1);
  519. //        g.setColor(oldfg);
  520.     }
  521.  
  522.     protected String chopString(Data data, Rectangle r, FontMetrics fm) {
  523.         String text = data.toString();
  524.         int w = view.getColumnWidth(coords.col);
  525.         chopIndex = 1;
  526.         String t;
  527.  
  528.         if (text.length() == 0) {
  529.             return text;
  530.         }
  531.         //chop off any part of string not visible
  532.         setOffset(text, r, fm);
  533.         if (offset != 0) {
  534.             int left;
  535.             while(toLeftOfCell < text.length()) {
  536.                 left = fm.stringWidth(text.substring(0, toLeftOfCell));
  537.                 if (left < offset + PADSIDES) {
  538.                     toLeftOfCell++;
  539.                 } else {
  540.                     toLeftOfCell--;
  541.                     break;  //found the visible portion of the text
  542.                 }
  543.             }
  544.  
  545.             toLeftOfCell -= (toLeftOfCell < text.length()) ?0  :1;
  546.             text = text.substring(toLeftOfCell, text.length());
  547.         } else {
  548.             toLeftOfCell = 0;
  549.         }
  550.  
  551.         //get the portion of string that fits into cell
  552.         do {
  553.             t = text.substring(0, chopIndex);
  554.         } while(fm.stringWidth(t) < w - PADSIDES*2 && chopIndex++ < text.length());
  555.  
  556.         return text.substring(0, --chopIndex);
  557.     }
  558.  
  559.     protected void drawText(Data data, Graphics g, Rectangle r,
  560.                             FontMetrics fm, CellHints hints)
  561.     {
  562.         //all highlighted and ready for delete
  563.         String text = chopString(data, r, fm);
  564.  
  565.         if (selected && !keyPressedYet) {
  566.             hints.setForeground(g);
  567.             g.fillRect(r.x-1,
  568.                        r.y+3,
  569.                        fm.stringWidth(text.toString())+4,
  570.                        fm.getHeight()-1);
  571.             hints.setBackground(g);
  572.             g.drawString(text, r.x, r.y+fm.getAscent()+2);
  573.         } else if (selected && !selectionMade) {
  574.             //draw string with cursor
  575.             g.drawString(text, r.x, r.y+fm.getAscent()+2);
  576.         } else if(!selected || !selectionMade ) {
  577.             //no highlight
  578.             g.drawString(text, r.x, r.y+fm.getAscent()+2);
  579.         } else {
  580.             //some section must be highlighted
  581.             int hlLeft = Math.min(hlFirst, hlLast);
  582.             int hlRight = Math.max(hlFirst, hlLast);
  583.             hlRight = Math.min(hlRight, chopIndex);
  584.  
  585.             if (toLeftOfCell != 0) {
  586.                 hlLeft = Math.max(hlFirst, toLeftOfCell);
  587.             }
  588.             String t = text.substring(0, hlLeft);
  589.  
  590.             //draw first unselected portion
  591.             g.drawString(t, r.x, r.y+fm.getAscent()+2);
  592.  
  593.             //draw middle selected portion
  594.             int pixelHLStart = fm.stringWidth(t) + r.x - offset;
  595.             t = text.substring(hlLeft, hlRight);
  596.             int pixelHLStop = fm.stringWidth(t) + pixelHLStart;
  597.             hints.setForeground(g);
  598.             g.fillRect(pixelHLStart,
  599.                        r.y+2,
  600.                        pixelHLStop - pixelHLStart,
  601.                        fm.getHeight()-1);
  602.             hints.setBackground(g);
  603.             g.drawString(t, pixelHLStart, r.y+fm.getAscent()+2);
  604.  
  605.             //draw end unselected portion
  606.             hints.setForeground(g);
  607.             t = text.substring(hlRight, text.length());
  608.             g.drawString(t, pixelHLStop, r.y+fm.getAscent()+2);
  609.         }
  610.     }
  611.  
  612.     void setOffset(String text, Rectangle r, FontMetrics fm) {
  613.         String sub = text.substring(0, cursorPos);
  614.         int toCursor = fm.stringWidth(sub);
  615.         int fudge = 3;
  616.  
  617.         if (!selected) {
  618.             offset = 0;
  619.             return;
  620.         }
  621.  
  622.         if (toCursor > r.width - fudge) {
  623.             offset = -r.width + toCursor + fudge;
  624.         } else {
  625.             offset = 0;
  626.         }
  627.     }
  628.  
  629.     int textLeft(int txtWidth, Rectangle r) {
  630.         switch(view.getCellAlignment(this)) {
  631.             case Grid.CENTER:
  632.                 if (txtWidth>r.width) {
  633.                     return r.x;
  634.                 } else {
  635.                     return r.x + (r.width-txtWidth)/2;
  636.                 }
  637.             case Grid.RIGHT:
  638.                 if (txtWidth>r.width) {
  639.                     return r.x + PADSIDES;
  640.                 } else {
  641.                     return r.x + r.width - txtWidth - PADSIDES;
  642.                 }
  643.             case Grid.LEFT:
  644.             default:
  645.                 return r.x + PADSIDES;
  646.         }
  647.     }
  648.  
  649.     /**
  650.      * Repaints the list when the cell's image has changed.
  651.      * @return true if image has changed; false otherwise.
  652.      */
  653.     public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {
  654.         if ((flags & (ABORT|ERROR)) != 0) {
  655.             return false;
  656.         }
  657.  
  658.         if ((flags & ALLBITS) != 0) {
  659.             view.redrawAsync();
  660.             return false;
  661.         } else {
  662.             return true;
  663.         }
  664.     }
  665.  
  666.     public String toString() {
  667.         try {
  668.             return readData().toString();
  669.         } catch(DataNotAvailable ex) {
  670.             return "ERROR getting data";
  671.         }
  672.     }
  673.  
  674.     public String stats() {
  675.         try {
  676.             return "BasicCell: row=" + coords.row + " col=" + coords.col +
  677.                 " text=" + readData().toString();
  678.         } catch(DataNotAvailable ex) {
  679.             return "BasicCell: row=" + coords.row + " col=" + coords.col +
  680.                    " could not retrieve data";
  681.         }
  682.     }
  683. }
  684.  
  685.